#-*- codeing = utf-8 -*-
#@Time : 2020/11/26 13:06
#@Author : 阳某
#@File : 05.SSH远程操作.py
#@Software : PyCharm


'''
SSH 远程操作
很多产品的服务端都是运行在 Linux 操作系统上的。

做 自动化测试 或者 运维工作的朋友，经常需要 控制远程 Linux 主机。

干什么事情呢？比如：自动安装环境 、执行自动化测试的一些步骤、数据的自动获取、分析等等。

Paramiko库简介
Python第三方库 Paramiko 就是作为ssh客户端远程控制Linux主机 的。

我们可以执行下面的命令

pip install paramiko
或者使用国内豆瓣的源

pip install paramiko –i https://pypi.douban.com/simple/ --trusted-host pypi.douban.com

要学习自动化操作远程Linux， 首先当然要有一台Linux主机

可以安装VirtualBox虚拟机，在里面安装Linux系统。参考我们的教程


有了Linux主机后，登录进去查看一下IP地址。

本文假设你的Linux主机IP地址是 192.168.2.100 ，用户名为 byhy，密码为 byhy5200


接下来，我们就可以使用Paramiko库，执行代码自动化连接，执行命令，就像人工操作一样。

import paramiko

# 创建SSHClient 实例对象
ssh = paramiko.SSHClient()

# 设置信任远程机器，允许访问
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# ssh连接远程机器，参数为  地址、端口、用户名、密码
ssh.connect("192.168.2.100", 22, "byhy", "byhy5200")

# 执行命令，创建目录 logs
ssh.exec_command("mkdir logs")

# 关闭ssh连接
ssh.close()
执行完上面的程序，就会发现远程的Linux主机，多出了一个目录，名为 logs

获取屏幕输出
自动化程序经常 要根据 输出的结果进行判断分析，然后决定下一步怎么做。

比如， 我们要杀死 服务进程， 必须执行ps命令 得到这个进程的ID，才能执行下面kill命令。

怎么获取呢？

可以像这样

cmd = 'ps -ef|grep apiserver |grep -v grep'

# 每次执行命令会返回3个对象，对应标准输入、标准输出、标准错误
stdin, stdout, stderr = ssh.exec_command(cmd)

# 从 标准输出、标准错误 中读取字节
outputBytes = stdout.read()+ stderr.read()

# 解码为字符串
outputStr = outputBytes.decode('utf8')

print(outstr)
ssh.exec_command(cmd) 会返回三个对象 stdin, stdout, stderr，

这三个对象的 类型 是 <class 'paramiko.channel.ChannelFile'>

非常类似我们前面学过的文件对象，分别对应了 远程执行程序 打开的标准输入，标准输出和 标准错误 三个设备文件。

那么如果我们获取了标准输出、和stderr对象，我们只要调用它的read方法就可以获取屏幕上输出的内容了。

命令的前后关联
大家执行一下下面这段代码

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.2.100", 22, "byhy", "byhy5200")

# 创建目录 testdir
ssh.exec_command("mkdir testdir")

# 进入目录 testdir
ssh.exec_command("cd testdir")

# 查看当前路径
stdin, stdout, stderr = ssh.exec_command("pwd")
print(stdout.read())

ssh.close()
大家会惊奇的发现打印处理目录不是 /home/byhy/testdir , 而是 /home/byhy/

难道 cd testdir 这命令没有起作用吗？


注意 exec_command 是每次执行都是 新打开一个channel的东西执行，

每个channel都是命令执行的环境，每执行命令都是一个新的执行环境，不在上次执行的环境里面，

相当于 每次都在各自的执行环境里面，和前面的执行环境没有关系。


那么怎么办呢？

可以： 多个命令一起执行，用分号隔开，像这样

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.2.100", 22, "byhy", "byhy5200")

# 创建目录 testdir
ssh.exec_command("mkdir testdir")

# 用一行命令 进入目录 testdir 并且 查看当前路径
stdin, stdout, stderr = ssh.exec_command("cd testdir;pwd")
print(stdout.read())

ssh.close()
文件传输
有时，我们需要和远程Linux机器之间互相传输文件，包括上传和下载。

比如 自动安装测试环境，经常需要把产品的安装包传送到远程机器上。

paramiko 库提供了 传送文件的方法，如下所示

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.2.100", 22, "byhy", "byhy5200")

# 打开一个sftp连接，返回sftp连接对象
sftp = ssh.open_sftp()

# put方法上传文件，第1个参数是本地路径，第2个参数是远程路径
sftp.put('install.zip', '/home/byhy/install.zip')

# get方法下载文件，第1个参数是远程路径，第2个参数是本地路径
sftp.get('/home/byhy/log.zip', 'd:/log.zip')

# 关闭sftp连接
sftp.close()

# 关闭ssh连接
ssh.close()

注意，

使用put方法上传文件时，第2个参数是远程路径，当前登录的用户必须有该路径的写权限

使用get方法上传文件时，第1个参数是远程路径，当前登录的用户必须有该路径的读权限

'''


import paramiko